package cpr.context;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import cpr.Printer;
import cpr.data.HLVector;
import cpr.data.SplitCluster;
import cpr.data.WorksetItem;
import cpr.helper.ScoreHelper;
import data.ClusterDescriptor;
import data.IClusterDescriptor;
import data.IWorksetItem;

public class WorksetContext implements IPurposeContext {
	//private List workSet;
	private WorksetItem root;
	private List<WorksetItem> wsItems;
	private Set splitCluster;
	private Context context;
	private String wsName;
	
	
	public WorksetContext(Context context, String name) {
		//workSet = new ArrayList();
		this.context = context;
		this.wsName = name;
		wsItems = new ArrayList<WorksetItem>();
	}
	
	public void close() {
		
	}
	
	public void clear() {
		wsItems.clear();
	}
	

	public String getWorksetFilePath() {
		String name = getContext().getRawDataFilePath() + ".workset" +"."+getWorksetName() ;
		return name;
	}
	
	public SolveContext createSolveContext(int worksetItem) {
		return new SolveContext(this,worksetItem);
	}
	public String getWorksetName() {
		return wsName;
	}
	
	public void setClusterSet(Set set) {
		splitCluster = set;
	}
	
//	public List getWorkSet() {
//		return workSet;
//	}
	
	public Set getClusterSet() {
		return splitCluster;
	}
	public void setWorksetItems(List items) {
		this.wsItems.clear();
		this.wsItems.addAll(items);
        for (Iterator iter = wsItems.iterator(); iter.hasNext();) {
          WorksetItem wsi = (WorksetItem) iter.next();
          addWorkSetItem(wsi);
        }
        
        
        
//    	getWorkSet().clear();
//    	for (Iterator iter = wsItems.iterator(); iter.hasNext();) {
//			IWorksetItem wsi = (IWorksetItem) iter.next();
//			SplitCluster sc = new SplitCluster(wsi.getClusterDescriptor().getRule(), wsi.getClusterDescriptor().getVector());
//			sc.setCounter(wsi.getClusterDescriptor().getSize());
//			getWorkSet().add(sc);
//		}
    }
	
	public WorksetItem addWorkSetItem(WorksetItem item) {
		for (Iterator iter = wsItems.iterator(); iter.hasNext();) {
			WorksetItem otherItem = (WorksetItem) iter.next();
			if (item!=otherItem) {
				if (ClusterDescriptor.comparable(item.getClusterDescriptor(), 
                        otherItem.getClusterDescriptor())) {
					if (ClusterDescriptor.compare(
                            item.getClusterDescriptor(), 
                            otherItem.getClusterDescriptor())>0) {
						if (item.getParent()==null) {
							item.setParent(otherItem);
                            otherItem.getChlids().add(item);
						} else {
							WorksetItem itemParent = item.getParent();
							if (ClusterDescriptor.comparable(otherItem.getClusterDescriptor(), itemParent.getClusterDescriptor())) {
								if (ClusterDescriptor.compare(otherItem.getClusterDescriptor(), itemParent.getClusterDescriptor())>0) {
                                    item.setParent(otherItem);
                                    otherItem.getChlids().add(item);
                                    itemParent.getChlids().remove(item);
								} else {
                                    
                                }
							} 
						}
                    } else if (ClusterDescriptor.compare(
                            item.getClusterDescriptor(), 
                            otherItem.getClusterDescriptor())==0){
                        
					} else {
                        // item < otherItem
                        WorksetItem otherItemParent = otherItem.getParent();
                        if (otherItemParent!=null) {
                            if (ClusterDescriptor.comparable(
                                    item.getClusterDescriptor(), 
                                    otherItemParent.getClusterDescriptor())) {
                                if (ClusterDescriptor.compare(
                                        item.getClusterDescriptor(), 
                                        otherItemParent.getClusterDescriptor())>0) {
                                    otherItem.setParent(item);
                                    item.getChlids().add(otherItem);
                                    otherItemParent.getChlids().remove(otherItem);
                                } else if (ClusterDescriptor.compare(
                                        item.getClusterDescriptor(), 
                                        otherItemParent.getClusterDescriptor())==0) {
                                } else {
                                    // do nothing
                                }
                            } else {
                                // not comparable
                                int t =121;
                            }
                        } else {
                            //item < otherItem
                            //otherItemParent == null
                            otherItem.setParent(item);
                            item.getChlids().add(otherItem);

                        }
                        
                        
                    }
				} else {
        
                    // not comparable
                    int t =2;
                }
			}
		}
        if (!wsItems.contains(item)) {
//            item.setId(wsItems.size());
            wsItems.add(item);
        }
		return item;
	}
	
	public void removeWorkSetItem(WorksetItem item) {
        if (item.getParent()!=null) {
            item.getParent().getChlids().remove(item);
        }
       // item.setId(-1);

		for (Iterator iter2 = wsItems.iterator(); iter2.hasNext();) {
			WorksetItem otherItem = (WorksetItem) iter2.next();
            if (item!=otherItem) {
                if (otherItem.getParent()==item) {
                    otherItem.setParent(item.getParent());
                    if (item.getParent()!=null) {
                        item.getParent().getChlids().add(otherItem);
                        
                    }
                }
            }
		}
        
		wsItems.remove(item);
	}
	
	
    public void TO_DELETE_update(){
    	List l = null;//getWorkSet();
		wsItems = new ArrayList();
		int i = 1;
		for (Iterator iter = l.iterator(); iter.hasNext();) {
			SplitCluster sc = (SplitCluster) iter.next();
			WorksetItem mi = new WorksetItem();
			//mi.setId(i++);
			mi.setClusterDescriptor(new ClusterDescriptor(sc.aRule, sc.aVector, sc._counter));
			wsItems.add(mi);
		} 
		
		for (Iterator iter = wsItems.iterator(); iter.hasNext();) {
			WorksetItem item = (WorksetItem) iter.next();
			for (Iterator iter2 = wsItems.iterator(); iter2.hasNext();) {
				WorksetItem item2 = (WorksetItem) iter2.next();
				if (!item.equals(item2)) {
					if (ClusterDescriptor.comparable(item.getClusterDescriptor(), item2.getClusterDescriptor())) {
						if (ClusterDescriptor.compare(item.getClusterDescriptor(), item2.getClusterDescriptor())>0) {
							if (item.getParent()==null) {
								item.setParent(item2);
							} else {
								IWorksetItem item3 = item.getParent();
								if (ClusterDescriptor.comparable(item2.getClusterDescriptor(), item3.getClusterDescriptor())) {
									if (ClusterDescriptor.compare(item2.getClusterDescriptor(), item3.getClusterDescriptor())>0) {
										item.setParent(item2);
									}
								} 
							}
						}
					}
				}
			}
		}
		root = new WorksetItem();
		int total_size = -1;
		root.setClusterDescriptor(new ClusterDescriptor(null, new HLVector(new int []{0,0,0,0,0}),total_size));
		for (Iterator iter = wsItems.iterator(); iter.hasNext();) {
			WorksetItem item = (WorksetItem) iter.next();
			if (item.getParent()!=null) {
				item.getParent().getChlids().add(item);
			} else {
				root.getChlids().add(item);
			}
		}
		wsItems.add(0, root);
    }

    public WorksetItem getWorksetItemRoot() {
        for (Iterator iter = wsItems.iterator(); iter.hasNext();) {
            WorksetItem item = (WorksetItem) iter.next();
            if (item.getParent()==null) {
                return item;
            }
        }
        return null;
    }

    public List<WorksetItem> getWorksetItems() {
    	return wsItems;
    }

    public int getIndexOf(WorksetItem  wsi) {
        return wsItems.indexOf(wsi);
    }
    
    public WorksetItem getWorksetItemAt(int i) {
    	return wsItems.get(i);
    }

	public Context getContext() {
		return context;
	}
    
	
	public String getSplitSequenceFilePath(String seqName) {
		if (seqName==null) {
			seqName = "default";
		}
		String name = getContext().getDefaultDataFolder()+ "split_seq" +"."+seqName ;
		return name;
	}
	private List splitSequence = null;
	private void loadSeqSequence(String seqName) {
		String filename = getSplitSequenceFilePath(seqName);
		Printer.println("Loading "+filename);
		File file = new File(filename);
		if (!file.exists()) {
			Printer.println("not found!");
			return;
		}
		try {
			Reader r = new FileReader(file);
			BufferedReader br = new BufferedReader(r);
			String s = null;
			Pattern spacePattern = Pattern.compile( "\\s" );
			int counter = 1;
			List items = new ArrayList();
			ScoreHelper helper = getContext().getScoreHelper();
			do {
				s= br.readLine();
				if ((s!=null)&&(!"".equals(s.trim()))){
					int score = helper.convertStringToScore(s);
					items.add(new HLVector(score));
				}
			} while(s!=null);
			br.close();
			splitSequence = items;
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
	}
	
	public void autoFill(String seqName) {
		
		loadSeqSequence(seqName);
		int p = 0;

        splitItem(0,null,0,0);
		
		boolean found = false;
		do {
			found = false;
			for (int i = 0; i < wsItems.size(); i++) {
				WorksetItem wsi = (WorksetItem) wsItems.get(i);
				int s =wsi.getClusterSize()-wsi.getChildSize();
	    		if (s==0) {
		    			found = true;
		    			removeWorkSetItem(wsi);
//		    			getWorkSet().remove(i);
//		    			update();
		    			break;
	    		}
			}
		} while(found);
	
		
		//update();
		
	}
    private static boolean DEBUG = false;
    private static int splitItemCounter = 0;
	public int splitItem(int p,IClusterDescriptor parent, int wsid, int lastWsi) {
        int currentInstance = splitItemCounter++;
        Printer.println(DEBUG,"\n#"+currentInstance+". splitItem: p="+p+" parent="+parent+" wsid="+wsid+" lastWsi="+lastWsi);
        
        int LOW_LIMIT = getContext().getSplitLowLimit();
		int LIMIT = getContext().getSplitLimit();
		if (splitSequence.size()<=p) {
			throw new RuntimeException("split_seq is too short. Current length="+splitSequence.size());
		}
		HLVector v = (HLVector)splitSequence.get(p);
		Set<SplitCluster> set = getContext().getSelectContext().selectSplitClusterSet(v,parent);
		for (Iterator iter = set.iterator(); iter.hasNext();) {
            SplitCluster sc =  (SplitCluster)iter.next();
            WorksetItem newWsi = new WorksetItem();
            newWsi.setClusterDescriptor(new ClusterDescriptor(sc.aRule, sc.aVector, sc._counter));
            if (newWsi.getClusterSize()>=LOW_LIMIT) {
                Printer.println(DEBUG,"adding item: "+newWsi.toString());
                newWsi = addWorkSetItem(newWsi);
                int s =newWsi.getClusterSize()-newWsi.getChildSize();
                Printer.println(DEBUG,"s="+s+" cluster-size="+newWsi.getClusterSize()+" childs-size="+newWsi.getChildSize());
                if (s<=LIMIT) {
                    break;
                }
            } else {
                Printer.println(DEBUG,"cluster is too small: "+newWsi.toString());
                break;
            }
        }
		int wsSize = wsItems.size();
//        Printer.println("wsSize="+wsSize);
        int lastAdded = wsSize;
		for (int i = lastWsi; i < wsSize; i++) {
			WorksetItem wsi = (WorksetItem) wsItems.get(i);
			if (wsi.getClusterSize()>LIMIT) {
                Printer.println(DEBUG,"req: p="+(p+1)+" parent="+wsi.getClusterDescriptor()+" i="+i+" wsSize="+wsSize+" lastAdded="+lastAdded);
                lastAdded = splitItem(p+1, wsi.getClusterDescriptor(), i,lastAdded);
			}
		}
        Printer.println(DEBUG,""+currentInstance+".------------------------\n");
		return lastAdded;
	}
	

}
